/**
 * @class Screenshot
 * @brief 截图工具。
 *
 * 截图原理：
 * 1 先抓取一个全屏图像
 * 2 再创建一个覆盖全屏的窗口，此窗口没有边框，必须是POPUP样式，以防macOS认为它是一个窗口，会出现工具栏及dock
 * 3 将图像贴到此窗口上
 * 4 鼠标移动时，根据鼠标的位置，获取对应的窗口，如果有窗口重叠，只选择最上面那个
 * 5 得到此窗口的矩形大小，并与截图对比，矩形之外的区域设置为半透明
 */

#include <QtWidgets>
#include <QApplication>
#include <QLabel>
#include <QQmlEngine>
#include <QJSEngine>

#include "../global/QmlNames.h"
#include "../multimedia/AudioPlayer.h"
#include "../config/config.h"
#include "Singleton.h"
#include "Screenshot.h"

X_BEGIN_NAMESPACE

// Class definition
Screenshot::Screenshot(QWidget *parent)
    : QWidget(parent)
    , m_screenshotLabel(new QLabel(this))
    , m_selecting(false)
    , m_corping(false)
{
    // 调试的时候可以使用，图片不会覆盖整个屏幕
    //m_screenshotLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    //m_screenshotLabel->setAlignment(Qt::AlignCenter);

    QVBoxLayout *mainLayout = new QVBoxLayout(this);
    mainLayout->setMargin(0);
    mainLayout->addWidget(m_screenshotLabel);

    // 需要使用virtualGeometry才能得到整个屏幕的尺寸
    QRect rec = QApplication::primaryScreen()->virtualGeometry();
    resize(rec.width(), rec.height());

    setWindowFlags(Qt::Popup | Qt::FramelessWindowHint);
    //setWindowFlags(Qt::Widget | Qt::FramelessWindowHint);
}
Screenshot::~Screenshot()
{

}

/**
 * @brief 为单例提供一个创建工厂
 *
 * @return
 */
Screenshot* Screenshot::createInstance()
{
    return new Screenshot(NULL);
}

/**
 * @brief 创建一个线程安全的单例对象。
 *
 * @return
 */
Screenshot& Screenshot::instance()
{
    return *Singleton<Screenshot>::instance(Screenshot::createInstance);
}

const char* Screenshot::qmlName()
{
    return QmlNames::Utils::ScreenshotSingleton;
}

QObject *Screenshot::qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine)
{
    Q_UNUSED(engine);
    Q_UNUSED(scriptEngine);

    return &instance();
}

/**
 * @brief 截图
 * @param hideMainWindow 截图前，是否隐藏应用程序本身的界面
 */
void Screenshot::shootScreen(bool hideMainWindow)
{
    m_hideMainWindow = hideMainWindow;

    shootScreen();
}

void Screenshot::resizeEvent(QResizeEvent * /* event */)
{
    QSize scaledSize = m_originalPixmap.size();
    scaledSize.scale(m_screenshotLabel->size(), Qt::KeepAspectRatio);
    if (!m_screenshotLabel->pixmap() || scaledSize != m_screenshotLabel->pixmap()->size())
        updateScreenshotLabel();
}

void Screenshot::keyPressEvent(QKeyEvent *event)
{
    if(event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return){
        apply();
    } else if(event->key() == Qt::Key_Escape){
        housekeep();
    }

    QWidget::keyPressEvent(event);
}

void Screenshot::mousePressEvent(QMouseEvent *event)
{
    m_selecting = true;

    m_startPoint = event->pos();
}

void Screenshot::mouseReleaseEvent(QMouseEvent *event)
{
    m_selecting = false;

    if(m_selecting){
        m_corping = true;
        m_endPoint = event->pos();
    }
}

void Screenshot::mouseDoubleClickEvent(QMouseEvent *event)
{
    if(m_selecting){
        apply();
    }
}

void Screenshot::mouseMoveEvent(QMouseEvent *event)
{
    if(m_selecting){
        m_endPoint = event->pos();
        updateScreenshotLabel();
    }
}

void Screenshot::apply()
{
    // 构建图片的文件名
    const QString fileName = Config::instance().storagePath(Config::Images) +
                             QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz") +
                             ".png";
    saveScreenshot(fileName);

    // 通知完成
    emit screenshotCompleted(fileName);

    // 清理
    housekeep();
}

/**
 * @brief 保存截图
 */
void Screenshot::saveScreenshot(const QString &filePath)
{
    QString fileName;

    if(filePath.length() == 0){
        const QString format = "png";
        QString initialPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
        if (initialPath.isEmpty())
            initialPath = QDir::currentPath();

        initialPath += tr("/untitled.") + format;

        QFileDialog fileDialog(this, tr("Save As"), initialPath);
        fileDialog.setAcceptMode(QFileDialog::AcceptSave);
        fileDialog.setFileMode(QFileDialog::AnyFile);
        fileDialog.setDirectory(initialPath);

        QStringList mimeTypes;
        foreach (const QByteArray &bf, QImageWriter::supportedMimeTypes())
            mimeTypes.append(QLatin1String(bf));
        fileDialog.setMimeTypeFilters(mimeTypes);
        fileDialog.selectMimeTypeFilter("image/" + format);
        fileDialog.setDefaultSuffix(format);
        if (fileDialog.exec() != QDialog::Accepted)
            return;

        fileName = fileDialog.selectedFiles().first();
    } else {
        fileName = filePath;
    }

    // 播放声音
    AudioPlayer::instance().playSystemSound(AudioPlayer::Screenshot);

    if (!m_originalPixmap.save(fileName)) {
        QMessageBox::warning(this, tr("保存截图"), tr("截图保存失败: \"%1\".")
                             .arg(QDir::toNativeSeparators(fileName)));
    }
}

/**
 * @brief 截图，并显示。
 */
void Screenshot::shootScreen()
{
    QScreen *screen = QGuiApplication::primaryScreen();
    if (const QWindow *window = windowHandle())
        screen = window->screen();

    if (!screen) return;

    // 显示
    m_originalPixmap = screen->grabWindow(0);
    m_scaleRatio = m_originalPixmap.rect().width() / m_screenshotLabel->rect().width();

    updateScreenshotLabel();

    show();
}

/**
 * @brief 关闭窗口，并清理原始图像，释放内存。
 */
void Screenshot::housekeep()
{
    close();

    m_screenshotLabel->clear();
}

/**
 * @brief 设置半透明并更新显示截图
 *
 * issue: 高DPI情况下，如Retina屏幕，缩放图片会有锯齿
 */
void Screenshot::updateScreenshotLabel()
{
    QRect corpRect =  m_selecting ? QRect(m_startPoint, m_endPoint) : QRect(0, 0, 0, 0);
    QPixmap target = renderPixmap(corpRect);

    m_screenshotLabel->setPixmap(target.scaled(m_screenshotLabel->size(),
                                               Qt::KeepAspectRatio,
                                               Qt::FastTransformation));
}

/**
 * @brief 渲染图片
 * @param rect 要排除渲染的区域
 * @return
 */
QPixmap Screenshot::renderPixmap(QRect &rect)
{
    rect = QRect(rect.top() * m_scaleRatio,
                 rect.left() * m_scaleRatio,
                 rect.bottom() * m_scaleRatio,
                 rect.right() * m_scaleRatio);

    QPixmap target = m_originalPixmap;

    // 围绕要排除的区域，计算四边的四个矩形区域
    QRect screenRect = target.rect();
    QRect top(screenRect.topLeft(), QPoint(rect.top(), screenRect.right()));
    QRect left(QPoint(rect.top(), screenRect.left()), QPoint(rect.bottom(), rect.left()));
    QRect right(QPoint(rect.top(), rect.right()), QPoint(rect.bottom(), screenRect.right()));
    QRect bottom(QPoint(rect.bottom(), screenRect.left()), QPoint(screenRect.bottomRight()));

    QList<QRect> rects;
    rects.push_back(top);
    rects.push_back(left);
    rects.push_back(right);
    rects.push_back(bottom);

    //
    QPainter painter(&target);
    painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
    foreach (QRect rect, rects) {
        painter.fillRect(rect, QColor(0, 0, 0, 125));    // 盖上一层蒙板
    }
    painter.end();

    return target;
}

X_END_NAMESPACE
